package com.spring.nio.core.reactor.acceptor;

import com.spring.nio.common.BasicConstant;
import com.spring.nio.core.event.model.EventModelEnum;
import com.spring.nio.util.NioUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketOption;
import java.net.UnknownHostException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;

/**
 * @author: YSFY
 * @date: 2021-03-24 18:26
 * @pacage_name: com.spring.nio.core.reactor
 * @class_name: AbstractAcceptor
 * @description: Acceptor公共处理类
 */

@Slf4j
@SuppressWarnings("all")
public abstract class AbstractNioAcceptor implements NioAcceptor {

    /**
     * 创建不同的ServerSocketChannel,
     * @param selector
     * @param assign
     * @return serverSocketChannel
     * @throws IOException
     * */
    protected ServerSocketChannel initServerSocketChannel(Selector selector, String assign) throws IOException {
        InetSocketAddress inetSocketAddress = this.create(assign);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        /**
         * 该选项用来决定如果网络上仍然有数据向旧的ServerSocket传输数据，是否允许新的ServerSocket绑定到与旧的ServerSocket同样的端口上，该选项的默认值与操作系统有关，在某些操作系统中，允许重用端口，而在某些系统中不允许重用端口。
         *
         *    当ServerSocket关闭时，如果网络上还有发送到这个serversocket上的数据，这个ServerSocket不会立即释放本地端口，而是等待一段时间，确保接收到了网络上发送过来的延迟数据，然后再释放端口。
         *
         *    值得注意的是，public void setReuseAddress(boolean on) throws SocketException必须在ServerSocket还没有绑定到一个本地端口之前使用，否则执行该方法无效。此外，两个公用同一个端口的进程必须都调用serverSocket.setReuseAddress(true)方法，才能使得一个进程关闭ServerSocket之后，另一个进程的ServerSocket还能够立刻重用相同的端口。
         * */
        serverSocketChannel.socket().setReuseAddress(Boolean.TRUE);
        serverSocketChannel.bind(inetSocketAddress);
        serverSocketChannel.configureBlocking(false);
        selector.wakeup();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        log.info(" AbstractNioAcceptor | --> [ " + assign + " ] ServerSocketChannel init success, waiting for client connect..., listener address = {}, thread = {}", (inetSocketAddress.toString()), Thread.currentThread().getName());
        return serverSocketChannel;
    }

    /**
     * 创建不同的InetSocketAddress
     * @param assign
     * @return serverSocketChannel
     * @throws UnknownHostException
     * */
    private InetSocketAddress create(String assign) throws UnknownHostException {
        String ip = NioUtil.getValue(BasicConstant.SERVER_IP);
        if(StringUtils.isBlank(ip) || StringUtils.isEmpty(ip)) {
            throw new RuntimeException("ServerSocektChannle Listener Ip is empty or blank");
        }

        String port = "";
        if(StringUtils.equals(AcceptorTypeEnum.CHAT_ACCEPTOR.getName(), assign)) {
            port = NioUtil.getValue(BasicConstant.NIO_MESSAGE_PORT);
        }

        if(StringUtils.equals(AcceptorTypeEnum.FILE_UPLOAD_ACCEPTOR.getName(), assign)) {
            port = NioUtil.getValue(BasicConstant.NIO_FILE_UPLOAD_PORT);
        }

        if(StringUtils.equals(AcceptorTypeEnum.FILE_DOWNLOAD_ACCEPTOR.getName(), assign)) {
            port = NioUtil.getValue(BasicConstant.NIO_FILE_DOWNLOAD_PORT);
        }

        /*if(StringUtils.equals(BasicConstant.NIO_SERVER_MAIN_CORE_WEBSOCKET_ACCEPTOR, assign)) {
            port = NioUtil.getValue(BasicConstant.NIO_WEBSOCKET_PORT);
        }*/

        if(StringUtils.isBlank(ip) || StringUtils.isEmpty(ip)) {
            throw new RuntimeException("ServerSocektChannle Listener port is empty or blank");
        }

        return new InetSocketAddress(InetAddress.getByName(ip), Integer.valueOf(port));
    }
}
